/**
  ETFDAQ Project
  \class TAController
  \brief A VME controller. Supposed to be a virtual class
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-08-12
  \date 2024-08-12 last modified

  \copyright Copyright (c) 2024 IMP-CAS with LGPLv3 LICENSE
*/

#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <sys/ioctl.h>
#include "TAController.h"
#include "TAMsg.h"
#include "TAYaml.h"
#include "sis3100_vme_calls.h"
#include "sis1100_var.h"

using std::cout;
using std::endl;

#define er TAMsg::Error

static void handler_irq(int sig){
  TAMsg::SetSignal(sig);
} // end handler handler_irq

TAController::TAController(const string &dev) : fName(dev), fd(-1), fD32(0),
fD16(0), fExtraDeadTime(0), fConfig(0), fTargetSIG(SIGUSR1){}

void TAController::Initialize(){
  if((fd = open(fName.data(), O_RDWR, 0)) < 0)
    er("TACtrler", "ctor: open %s error: %s", fName.data(), strerror(errno));
  Reset();
  TAMsg::Info("TACtrler", "%s opened, fd = %d, pid = %d", fName.data(), fd, getpid());

  if(fConfig && (*fConfig)["controller"]){
    const TAYaml &cfg = (*fConfig)["controller"];
    // if(!cfg) er("TACtrler", "Initialize: \'controller\' in yaml file undefined");
    if(cfg["extraDeadTime"]) fExtraDeadTime = cfg["extraDeadTime"].as<int>();
    if(fExtraDeadTime < 0) fExtraDeadTime = 0;
    TAMsg::Info("TACtrler", "extra dead time: %d us", fExtraDeadTime);
  } // end if

  // have to block fTargetSIG signal, or the program will be terminated in tasks
  // other than waiting for an interrupt
  if(-1 == sigemptyset(&fSigMask)) perror("TACtrler::ctor, sigemptyset0");
  if(-1 == sigaddset(&fSigMask, fTargetSIG)) perror("TACtrler::ctor, sigaddset");
  // this is necessary to avoid the process being killed by SIG from trig in the
  // time other than the DAQ loop -- while within the DAQ loop, it is the fOldSigMask
  // that specifies which signals are blocked
  if(-1 == sigprocmask(SIG_BLOCK, &fSigMask, &fOldSigMask))
    perror("TACtrler::ctor, sigprocmask");
  if(-1 == sigdelset(&fOldSigMask, fTargetSIG)) perror("TACtrler::ctor, sigdelset");
  // capture fTargetSIG signal, or the program terminates upon this signal, too
  if(-1 == sigemptyset(&fSigAction.sa_mask)) perror("TACtrler::ctor, sigemptyset1");
  fSigAction.sa_flags = 0;
  fSigAction.sa_handler = handler_irq;
  if(-1 == sigaction(fTargetSIG, &fSigAction, &fOldSigAction))
    perror("TACtrler::ctor, sigaction");
} // end member function Initialize

TAController::~TAController(){
  if(fd >= 0){ // initialize() has been called
    if(-1 == sigaction(fTargetSIG, &fOldSigAction, 0)) // recover reaction
      perror("TACtrler::dtor, sigaction");
    if(-1 == sigprocmask(SIG_SETMASK, &fOldSigMask, 0)) // recover sig mask
      perror("TACtrler::dtor, sigprocmask");
    if(close(fd) < 0)
      er("TACtrler", "dtor: close %s error: %s", fName.data(), strerror(errno));
  } // end if
} // end dtor

// block until an IRQ arrives
void TAController::SuspendForIRQ(){
  sigsuspend(&fOldSigMask);
} // end member function SuspendForIRQ
void TAController::printsig(sigset_t &s){
  cout << "0b ";
  for(int i = 32; i--;){
    const int res = sigismember(&s, i);
    if(-1 == res){
      perror("pringsig, sigismember");
      exit(1);
    } // end if
    if(1 == res) cout << '1';
    else cout << '0';
    if(i%8 == 0) cout << "  ";
    else if(i%4 == 0) cout << ' ';
  } // end for over i
  cout << endl;
} // end member function printsig

// print fD32 in binary form
void TAController::PrintD32InBinaryForm() const{
  TAMsg::Print32(fD32);
} // end member function PrintD32InBinaryForm
// print fD16 in binary form
void TAController::PrintD16InBinaryForm() const{
  TAMsg::Print32(fD16);
} // end member function PrintD16InBinaryForm

// whether an IRQ has arrived
bool TAController::IsIRQed() const{
  return TAMsg::Signal() == fTargetSIG;
} // end member function IsIRQed
